package de.cau.apo.problems.strippacking;

import java.util.Arrays;
import java.util.Comparator;

import de.cau.apo.exceptions.NotFeasibleException;
import de.cau.apo.problems.AbstractSolution;

/**
 * This class represents a possible solution of the strip packing problem.
 * It uses the rectangle objects of its corresponding instance object and is 
 * responsive for checking the feasibility and calculating the objective value.
 * 
 * @author Marius Richter
 *
 */
public class SPSolution extends AbstractSolution<SPInstance> {

	// ########### instantiation ###########
	
	/**
     * Creates a new empty solution for the strip packing problem.
     * @param instance the instance for that this solution shall be created
     */
	public SPSolution(SPInstance instance) {
		super(instance);
	}
	
	// ########### getter/setter ###########
	
	/**
     * clear the positions of all strips
     */
    public void clear() {
        for (Rectangle rect : this.getInstance().getRectangles()) {
            rect.setLocation(-1, -1);
        }
    }

	// ########### ISolution implementation ###########
	
    /**
	 * {@inheritDoc}
	 */
	@Override
	public void decode(String solution) {
		String[] positions = solution.split(" ");
		for (int i = 0; i < positions.length; i += 2) {
			Rectangle rect = this.getInstance().getRectangle(i/2);
			rect.setX(Float.parseFloat(positions[i]));
			rect.setY(Float.parseFloat(positions[i+1]));
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String encode() {
		return this.toString();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public double getObjValue() {
		float height = 0;
		for (Rectangle rect : this.getInstance().getRectangles()) {
			if (rect.getX() < 0 || rect.getY() < 0) continue;
			height = Math.max(height, rect.getX() + rect.getHeight());
		}
		return height;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void isFeasible() throws NotFeasibleException {
		for (Rectangle rect : this.getInstance().getRectangles()) {
			if (rect.getX() < 0 || rect.getY() < 0) {
				throw new NotFeasibleException(rect + " has an invalid position");
			} else if (rect.getX() + rect.getWidth() > this.getInstance().getMaxStripWidth()) {
				throw new NotFeasibleException(rect + " exceeds the maximum strip width");
			} else {
				for (Rectangle other : this.getInstance().getRectangles()) {
					if (other.equals(rect)) continue;
					float rectRightEdge = rect.getX() + rect.getWidth();
					float rectTopEdge = rect.getY() + rect.getHeight();
					float otherRightEdge = other.getX() + other.getWidth();
					float otherTopEdge = other.getY() + other.getHeight();
					if (rectRightEdge > other.getX() && otherRightEdge > rect.getX()) {
						if (rectTopEdge > other.getY() && otherTopEdge > rect.getY()) {
							throw new NotFeasibleException(rect + " intersects with " + other);
						}
					} else if (rectTopEdge > other.getY() && otherTopEdge > rect.getY()) {
						if (rectRightEdge > other.getX() && otherRightEdge > rect.getX()) {
							throw new NotFeasibleException(rect + " intersects with " + other);
						}
					}
				}
			}
		}
		
	}

	// ########### overriding toString() ###########
	
	@Override
	public String toString() {
		String string = "";

        Rectangle[] rectangles = Arrays.copyOf(this.getInstance().getRectangles(), this.getInstance().getNumberRectangles());
        Arrays.sort(rectangles, new Comparator<Rectangle>(){
			@Override
			public int compare(Rectangle arg0, Rectangle arg1) {
				return arg0.index - arg1.index;
			}
        });
        
        for (Rectangle rect : rectangles) {
        	// printing the x coordinate
        	if (rect.getX() < 0) {
        		string += "# ";
        	} else {
        		string += rect.getX() + " ";
        	}
        	// printing the y coordinate
        	if (rect.getY() < 0) {
        		string += "# ";
        	} else {
        		string += rect.getY() + " ";
        	}
        }
		return string;
	}

}
